local string_find = string.find
local string_match = string.match
local string_len = string.len
local string_sub = string.sub
local string_replace = string.replace
local string_lower = string.lower
local string_format = string.format
local pairs = pairs
local next = next
local tonumber = tonumber
local select = select

local SEPARATORS, PATTERNS, STATS
if GetLocale() == "enUS" then
	SEPARATORS = {
		[","] = true,
		[" and "] = true,
		["/"] = true,
		[". "] = true,
	--	[" for "] = true,
		["&"] = true,
	--	[":"] = true,
	}
	PATTERNS = {
		[" by (%d+)"] = true,
		["([%+%-]%d+)"] = false,
		["grant.-(%d+)"] = true,
		["add.-(%d+)"] = true,
		["(%d+)([^%d%%|]+)"] = false,
	}
	STATS = {
		{"defense rating", CR_DEFENSE_SKILL},
		{"dodge rating", CR_DODGE},
		{"block rating", CR_BLOCK},
		{"parry rating", CR_PARRY},
		{"spell critical strike rating", CR_CRIT_SPELL},
		{"spell critical hit rating", CR_CRIT_SPELL},
		{"spell critical rating", CR_CRIT_SPELL},
		{"spell crit rating", CR_CRIT_SPELL},
		{"ranged critical strike rating", CR_CRIT_RANGED},
		{"ranged critical hit rating", CR_CRIT_RANGED},
		{"ranged critical rating", CR_CRIT_RANGED},
		{"ranged crit rating", CR_CRIT_RANGED},
		{"critical strike rating", CR_CRIT_MELEE},
		{"critical hit rating", CR_CRIT_MELEE},
		{"critical rating", CR_CRIT_MELEE},
		{"crit rating", CR_CRIT_MELEE},
		{"spell hit rating", CR_HIT_SPELL},
		{"ranged hit rating", CR_HIT_RANGED},
		{"hit rating", CR_HIT_MELEE},
		{"resilience", CR_CRIT_TAKEN_MELEE},
		{"spell haste rating", CR_HASTE_SPELL},
		{"ranged haste rating", CR_HASTE_RANGED},
		{"haste rating", CR_HASTE_MELEE},
		{"skill rating", CR_WEAPON_SKILL},
		{"expertise rating", CR_EXPERTISE},
		{"hit avoidance rating", CR_HIT_TAKEN_MELEE},
	}
elseif GetLocale() == "frFR" then
	SEPARATORS = {
		["/"] = true,
		[" et "] = true, 
		[","] = true, 
		[". "] = true,
	}
	PATTERNS = {
		[" de (%d+)"] = true,
		["([%+%-]%d+)"] = false,
		["(%d+) \195\160 "] = true,
		["(%d+) au"] = false,
	}
	STATS = {
		{"score de défense", CR_DEFENSE_SKILL},
		{"score d'esquive", CR_DODGE},
		{"score de blocage", CR_BLOCK},
		{"score de parade", CR_PARRY},
		{"score de coups critique des sorts", CR_CRIT_SPELL},
		{"score de critique des sorts", CR_CRIT_SPELL},
--~ 		{"spell critical rating", CR_CRIT_SPELL},
--~ 		{"spell crit rating", CR_CRIT_SPELL},
		{"score de coup critique à distance", CR_CRIT_RANGED},
		{"score de critique à distance", CR_CRIT_RANGED},
--~ 		{"ranged critical rating", CR_CRIT_RANGED},
--~ 		{"ranged crit rating", CR_CRIT_RANGED},
		{"score de coup critique", CR_CRIT_MELEE},
		{"score de critique", CR_CRIT_MELEE},
--~ 		{"critical rating", CR_CRIT_MELEE},
--~ 		{"crit rating", CR_CRIT_MELEE},
		{"score de toucher des sorts", CR_HIT_SPELL},
		{"score de toucher à distance", CR_HIT_RANGED},
		{"score de toucher", CR_HIT_MELEE},
		{"score de résilience", CR_CRIT_TAKEN_MELEE},
		{"résilience", CR_CRIT_TAKEN_MELEE},
		{"score de hâte des sorts", CR_HASTE_SPELL},
		{"score de hâte à distance", CR_HASTE_RANGED},
		{"score de hâte", CR_HASTE_MELEE},
		{"score de la compétence", CR_WEAPON_SKILL},
		{"score d'expertise", CR_EXPERTISE},
		{"expertise", CR_EXPERTISE},
		{"score d'évitement des coups", CR_HIT_TAKEN_MELEE},
	}
end

local Ratings = {}

do
	local hook_otsi = function (...) Ratings:OnTooltipSetItem(...) end
	local hook_otc = function (...) Ratings:OnTooltipCleared(...) end
	function Ratings:HookTooltip(tt)
		if tt:GetScript("OnTooltipSetItem") then
			tt:HookScript("OnTooltipSetItem", hook_otsi)
		else
			tt:SetScript("OnTooltipSetItem", hook_otsi)	
		end
		if tt:GetScript("OnTooltipCleared") then
			tt:HookScript("OnTooltipCleared", hook_otc)
		else
			tt:SetScript("OnTooltipCleared", hook_otc)	
		end
	end
end

do
	function Ratings:OnTooltipSetItem(tt)
		tt.ratings_set = true
		self:HandleFontStrings(tt:GetRegions())
	end
	
	function Ratings:OnTooltipCleared(tt)
		tt.ratings_set = nil
	end
end

function Ratings:HandleFontStrings(...)
	for i = 1, select("#", ...) do
		local region = select(i, ...)
		if region.GetText and region:IsShown() then
			region:SetText(self:ReplaceText(region:GetText()))
		end
	end
end

do
	local function get_next_chunck(text, p)
		local lstart, lend
		for sep in pairs(SEPARATORS) do
			local s, e = string_find(text, sep, p, true)
			if s then
				if not lstart or lstart > s then
					lstart, lend = s, e
				end
			end
		end
		if not lstart then
			return string_sub(text, p), -1
		else
			return string_sub(text, p, lstart - 1), lend + 1
		end
	end

	local replacements = {}
	function Ratings:ReplaceText(text)
		if not text or not text:find("%d") then return text end
		local pos = 1
		while pos > 0 do
			local sub
			sub, pos = get_next_chunck(text, pos)
			replacements[sub] = self:GetReplacementText(sub)
		end
		while true do
			local sub, rep = next(replacements)
			if not sub then return text end
			replacements[sub] = nil
			text = string_replace(text, sub, rep)
		end
	end
	
	function Ratings:GetReplacementText(text)
		local lower_text = string_lower(text)
		for pattern, after in pairs(PATTERNS) do
			local value, partial = string_match(lower_text, pattern)
			if value then
				if tonumber(partial) then
					partial, value = value, partial
				end
				local check = partial or lower_text
				for _, info in pairs(STATS) do
					local stat, id = info[1], info[2]
					if string_find(check, stat, 1, true) then
						local bonus = self:GetRatingBonus(id, tonumber(value))
						if not bonus then return end
						if after then
							return string_replace(text, value, string_format("%s (%s)", value, bonus))
						else
							local s, e = string_find(string_lower(text), stat, 1, true)
							stat = string_sub(text, s, e)
							return string_replace(text, stat, string_format("%s (%s)", stat, bonus))
						end
					end
				end
			end
		end
	end
end

do
	local CombatRatingMap = {
		[CR_WEAPON_SKILL] = 2.5,
		[CR_DEFENSE_SKILL] = 1.5,
		[CR_DODGE] = 12,
		[CR_PARRY] = 15,
		[CR_BLOCK] = 5,
		[CR_HIT_MELEE] = 10,
		[CR_CRIT_MELEE] = 14,
		[CR_HIT_RANGED] = 10,
		[CR_CRIT_RANGED] = 14,
		[CR_HASTE_MELEE] = 10,
		[CR_HASTE_RANGED] = 10,
		[CR_HIT_SPELL] = 8,
		[CR_CRIT_SPELL] = 14,
		[CR_HASTE_SPELL] = 10,
		[CR_CRIT_TAKEN_MELEE] = 25,
		[CR_HIT_TAKEN_MELEE] = 10,
		[CR_EXPERTISE] = 2.5,
	}
	
	local function GetRatingMultiplier()
		local level = UnitLevel("player")
		if level < 10 then
			return 26
		elseif level <= 60 then
			return 52 / (level - 8)
		elseif level <= 70 then
			-- return 1 - (level - 60) * 3 / 82
			return 3.1951219512195124 - 0.036585365853658534 * level
		end
	end
	
	function Ratings:GetRatingBonus(type, value)
		local F = CombatRatingMap[type]
		if not F then return end
		local bonus = value / F * GetRatingMultiplier()
		if type <= 2 then
			return string_format("%.2f", bonus)
		else
			return string_format("%.2f%%", bonus)
		end
	end
end

Ratings:HookTooltip(GameTooltip)
Ratings:HookTooltip(ItemRefTooltip)
Ratings:HookTooltip(ShoppingTooltip1)
Ratings:HookTooltip(ShoppingTooltip2)

_G.Ratings = Ratings
